package com.ailink.sap.xml;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 序列化
 */
public class XmlSerialize {

    private boolean isHaveHead;
    private Object object;

    private String head = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";

    public XmlSerialize(Object object, boolean isHaveHead) {
        this.object = object;
        this.isHaveHead = isHaveHead;
    }

    public String serialize() {
        StringBuilder value = search(this.object);
        XmlNode xmlNode = this.object.getClass().getAnnotation(XmlNode.class);
        XmlInfo xmlInfo = getXmlInfo(xmlNode, value);

        if (isHaveHead) {
            return head + xmlInfo.getText();
        } else {
            return xmlInfo.getText();
        }
    }

    private StringBuilder search(Object object) {
        if (object == null) {
            return null;
        }
        try {
            StringBuilder allXml = new StringBuilder();
            Field[] fields = object.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                XmlNode xmlNode = field.getAnnotation(XmlNode.class);
                Cdata cdData = field.getAnnotation(Cdata.class);
                if (xmlNode != null) {
                    field.setAccessible(true);
                    Object o = field.get(object);
                    if (o != null && o instanceof List) {
                        List oList = (List) o;
                        for (Object value : oList) {
                            StringBuilder nowXml = search(value);
                            process(allXml, xmlNode, o, nowXml, cdData);
                        }
                    } else {
                        StringBuilder temp = search(o);
                        process(allXml, xmlNode, o, temp, cdData);
                    }
                }
            }
            return allXml;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 拼接
     * @param allXml all xml
     * @param xmlNode xmlNode
     * @param object value
     * @param nowXml now xml
     * @param cdData cdData
     */
    private void process(StringBuilder allXml, XmlNode xmlNode, Object object, StringBuilder nowXml, Cdata cdData) {
        Object value;
        if (object == null) {
            value = null;
        } else if (String.class.equals(object.getClass())) {
            if (cdData != null) {
                value = new StringBuilder().append("<![CDATA[").append(object).append("]]>");
            } else {
                value = object;
            }
        } else {
            value = nowXml.toString();
        }
        XmlInfo xmlInfo = getXmlInfo(xmlNode, value);
        allXml.append(xmlInfo.getText());
    }

    private XmlInfo getXmlInfo(XmlNode xmlNode, Object value) {
        StringBuilder xmlName = new StringBuilder();
        List<StringBuilder> xmlNamespace = new ArrayList<>();
        if (!StringUtils.isEmpty(xmlNode.prefix())) {
            xmlName.append(xmlNode.prefix()).append(Xml.SPLIT);
        }
        xmlName.append(xmlNode.name());
        for (int j = 0; j < xmlNode.namespaces().length; j++) {
            xmlNamespace.add(new StringBuilder()
                    .append(xmlNode.namespaces()[j].name())
                    .append(Xml.SPLIT)
                    .append(xmlNode.namespaces()[j].prefix())
                    .append(Xml.EQUAL)
                    .append("\"")
                    .append(xmlNode.namespaces()[j].uri())
                    .append("\""));
        }

        XmlInfo xmlInfo = new XmlInfo();
        if (value != null) {
            xmlInfo.setUp(getUp(xmlName, xmlNamespace, false));
            xmlInfo.setDown(getDown(xmlName));
            xmlInfo.setValue(new StringBuilder(value.toString()));
        } else {
            xmlInfo.setUp(getUp(xmlName, xmlNamespace, true));
        }

        return xmlInfo;
    }

    private StringBuilder getDown(StringBuilder xmlName) {
        StringBuilder temp = new StringBuilder();
        temp.append("</");
        temp.append(xmlName);
        temp.append(">");
        return temp;
    }

    private StringBuilder getUp(StringBuilder xmlName, List<StringBuilder> xmlNamespace, boolean valueIsNull) {
        StringBuilder temp = new StringBuilder();
        temp.append("<");
        temp.append(xmlName);
        for (StringBuilder value : xmlNamespace) {
            temp.append(Xml.SPACE);
            temp.append(value);
        }
        if (!valueIsNull) {
            temp.append(">");
        } else {
            temp.append("/>");
        }
        return temp;
    }
}
